﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using UIT.Converters;
using System.Globalization;
using UIT.Utils;

namespace UIT.Style
{
    /// <summary>Describes the thickness of a frame around a rectangle. Four <see cref="T:System.Double" /> values describe the <see cref="P:System.Windows.Thickness.Left" />, <see cref="P:System.Windows.Thickness.Top" />, <see cref="P:System.Windows.Thickness.Right" />, and <see cref="P:System.Windows.Thickness.Bottom" /> sides of the rectangle, respectively. </summary>
    [TypeConverter(typeof(ThicknessConverter))]
    public struct Thickness : IEquatable<Thickness>, INotifyPropertyChanged
    {
        private Unit _Left;
        private Unit _Top;
        private Unit _Right;
        private Unit _Bottom;
        public bool IsZero
        {
            get
            {
                return DoubleUtil.IsZero(this.Left.Value) && DoubleUtil.IsZero(this.Top.Value) && DoubleUtil.IsZero(this.Right.Value) && DoubleUtil.IsZero(this.Bottom.Value);
            }
        }
        public bool IsUniform
        {
            get
            {
                return DoubleUtil.AreClose(this.Left.Value, this.Top.Value) && DoubleUtil.AreClose(this.Left.Value, this.Right.Value) && DoubleUtil.AreClose(this.Left.Value, this.Bottom.Value);
            }
        }
        /// <summary>Gets or sets the width, in pixels, of the left side of the bounding rectangle. </summary>
        /// <returns>A <see cref="T:System.Double" /> that represents the width, in pixels, of the left side of the bounding rectangle for this instance of <see cref="T:System.Windows.Thickness" />. a pixel is equal to 1/96 on an inch. The default is 0.</returns>
        public Unit Left
        {
            get
            {
                return this._Left;
            }
            set
            {
                this._Left = value;
                OnPropertyChanged("Left");
            }
        }
        /// <summary>Gets or sets the width, in pixels, of the upper side of the bounding rectangle.</summary>
        /// <returns>A <see cref="T:System.Double" /> that represents the width, in pixels, of the upper side of the bounding rectangle for this instance of <see cref="T:System.Windows.Thickness" />. A pixel is equal to 1/96 of an inch. The default is 0.</returns>
        public Unit Top
        {
            get
            {
                return this._Top;
            }
            set
            {
                this._Top = value;
                OnPropertyChanged("Top");
            }
        }
        /// <summary>Gets or sets the width, in pixels, of the right side of the bounding rectangle. </summary>
        /// <returns>A <see cref="T:System.Double" /> that represents the width, in pixels, of the right side of the bounding rectangle for this instance of <see cref="T:System.Windows.Thickness" />. A pixel is equal to 1/96 of an inch. The default is 0.</returns>
        public Unit Right
        {
            get
            {
                return this._Right;
            }
            set
            {
                this._Right = value;
                OnPropertyChanged("Right");
            }
        }
        /// <summary>Gets or sets the width, in pixels, of the lower side of the bounding rectangle.</summary>
        /// <returns>A <see cref="T:System.Double" /> that represents the width, in pixels, of the lower side of the bounding rectangle for this instance of <see cref="T:System.Windows.Thickness" />. A pixel is equal to 1/96 of an inch. The default is 0.</returns>
        public Unit Bottom
        {
            get
            {
                return this._Bottom;
            }
            set
            {
                this._Bottom = value;
                OnPropertyChanged("Bottom");
            }
        }
        /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Thickness" /> structure that has the specified uniform length on each side. </summary>
        /// <param name="uniformLength">The uniform length applied to all four sides of the bounding rectangle.</param>
        public Thickness(double uniformLength)
            : this(new Unit(uniformLength))
        {
        }

        /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Thickness" /> structure that has specific lengths (supplied as a <see cref="T:System.Double" />) applied to each side of the rectangle. </summary>
        /// <param name="left">The thickness for the left side of the rectangle.</param>
        /// <param name="top">The thickness for the upper side of the rectangle.</param>
        /// <param name="right">The thickness for the right side of the rectangle</param>
        /// <param name="bottom">The thickness for the lower side of the rectangle.</param>
        public Thickness(double left, double top, double right, double bottom)
            : this(new Unit(left), new Unit(top), new Unit(right), new Unit(bottom))
        {
        }


        /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Thickness" /> structure that has the specified uniform length on each side. </summary>
        /// <param name="uniformLength">The uniform length applied to all four sides of the bounding rectangle.</param>
        public Thickness(Unit uniformLength)
            : this(uniformLength, uniformLength, uniformLength, uniformLength)
        {
        }


        /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Thickness" /> structure that has specific lengths (supplied as a <see cref="T:System.Double" />) applied to each side of the rectangle. </summary>
        /// <param name="left">The thickness for the left side of the rectangle.</param>
        /// <param name="top">The thickness for the upper side of the rectangle.</param>
        /// <param name="right">The thickness for the right side of the rectangle</param>
        /// <param name="bottom">The thickness for the lower side of the rectangle.</param>
        public Thickness(Unit left, Unit top, Unit right, Unit bottom)
        {
            this._Left = left;
            this._Top = top;
            this._Right = right;
            this._Bottom = bottom;
            this.PropertyChanged = null;
        }
        /// <summary>Compares this <see cref="T:System.Windows.Thickness" /> structure to another <see cref="T:System.Object" /> for equality.</summary>
        /// <returns>true if the two objects are equal; otherwise, false.</returns>
        /// <param name="obj">The object to compare.</param>
        public override bool Equals(object obj)
        {
            if (obj is Thickness)
            {
                Thickness t = (Thickness)obj;
                return this == t;
            }
            return false;
        }
        /// <summary>Compares this <see cref="T:System.Windows.Thickness" /> structure to another <see cref="T:System.Windows.Thickness" /> structure for equality.</summary>
        /// <returns>true if the two instances of <see cref="T:System.Windows.Thickness" /> are equal; otherwise, false.</returns>
        /// <param name="thickness">An instance of <see cref="T:System.Windows.Thickness" /> to compare for equality.</param>
        public bool Equals(Thickness thickness)
        {
            return this == thickness;
        }
        /// <summary>Returns the hash code of the structure.</summary>
        /// <returns>A hash code for this instance of <see cref="T:System.Windows.Thickness" />.</returns>
        public override int GetHashCode()
        {
            return this._Left.GetHashCode() ^ this._Top.GetHashCode() ^ this._Right.GetHashCode() ^ this._Bottom.GetHashCode();
        }
        /// <summary>Returns the string representation of the <see cref="T:System.Windows.Thickness" /> structure.</summary>
        /// <returns>A <see cref="T:System.String" /> that represents the <see cref="T:System.Windows.Thickness" /> value.</returns>
        public override string ToString()
        {
            return ToString(CultureInfo.InvariantCulture);
        }
        internal string ToString(CultureInfo cultureInfo)
        {
            return ThicknessConverter.ToString(this, cultureInfo);
        }
        internal bool IsValid(bool allowNegative, bool allowNaN, bool allowPositiveInfinity, bool allowNegativeInfinity)
        {
            return (allowNegative || (this.Left.Value >= 0.0 && this.Right.Value >= 0.0 && this.Top.Value >= 0.0 && this.Bottom.Value >= 0.0)) &&
                (allowNaN || (!DoubleUtil.IsNaN(this.Left.Value) && !DoubleUtil.IsNaN(this.Right.Value) && !DoubleUtil.IsNaN(this.Top.Value) && !DoubleUtil.IsNaN(this.Bottom.Value))) &&
                (allowPositiveInfinity || (!double.IsPositiveInfinity(this.Left.Value) && !double.IsPositiveInfinity(this.Right.Value) && !double.IsPositiveInfinity(this.Top.Value) && !double.IsPositiveInfinity(this.Bottom.Value))) &&
                (allowNegativeInfinity || (!double.IsNegativeInfinity(this.Left.Value) && !double.IsNegativeInfinity(this.Right.Value) && !double.IsNegativeInfinity(this.Top.Value) && !double.IsNegativeInfinity(this.Bottom.Value))) &&
                this.Left.Type != UnitType.Percentage && this.Top.Type != UnitType.Percentage && this.Right.Type != UnitType.Percentage && this.Bottom.Type != UnitType.Percentage;
        }
        internal bool IsClose(Thickness thickness)
        {
            return DoubleUtil.AreClose(this.Left.Value, thickness.Left.Value) && DoubleUtil.AreClose(this.Top.Value, thickness.Top.Value) && DoubleUtil.AreClose(this.Right.Value, thickness.Right.Value) && DoubleUtil.AreClose(this.Bottom.Value, thickness.Bottom.Value);
        }
        internal static bool AreClose(Thickness thickness0, Thickness thickness1)
        {
            return thickness0.IsClose(thickness1);
        }
        /// <summary>Compares the value of two <see cref="T:System.Windows.Thickness" /> structures for equality.</summary>
        /// <returns>true if the two instances of <see cref="T:System.Windows.Thickness" /> are equal; otherwise, false.</returns>
        /// <param name="t1">The first structure to compare.</param>
        /// <param name="t2">The other structure to compare.</param>
        public static bool operator ==(Thickness t1, Thickness t2)
        {
            return (t1._Left == t2._Left || (DoubleUtil.IsNaN(t1._Left.Value) && DoubleUtil.IsNaN(t2._Left.Value))) && (t1._Top == t2._Top || (DoubleUtil.IsNaN(t1._Top.Value) && DoubleUtil.IsNaN(t2._Top.Value))) && (t1._Right == t2._Right || (DoubleUtil.IsNaN(t1._Right.Value) && DoubleUtil.IsNaN(t2._Right.Value))) && (t1._Bottom == t2._Bottom || (DoubleUtil.IsNaN(t1._Bottom.Value) && DoubleUtil.IsNaN(t2._Bottom.Value)));
        }
        /// <summary>Compares two <see cref="T:System.Windows.Thickness" /> structures for inequality. </summary>
        /// <returns>true if the two instances of <see cref="T:System.Windows.Thickness" /> are not equal; otherwise, false.</returns>
        /// <param name="t1">The first structure to compare.</param>
        /// <param name="t2">The other structure to compare.</param>
        public static bool operator !=(Thickness t1, Thickness t2)
        {
            return !(t1 == t2);
        }

        #region INotifyPropertyChanged Members

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
